////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#ifndef PARSER_HPP
#define PARSER_HPP

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <istream>
using std::istream;

#include <string>
using std::string;

#include <fstream>
using std::ifstream;

#include <sstream>
using std::istringstream;

#include <typeinfo>
using std::type_info;

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;
using boost::dynamic_pointer_cast;

#include <boost/scoped_ptr.hpp>
using boost::scoped_ptr;

#include "lexer.lex.hpp"
#include "bisonparser.tab.hpp"
#include "common.hpp"
#include "exception.hpp"
#include "messages.hpp"
#include "assert.hpp"

#include "ast/declarations.hpp"
#include "ast/type.hpp"
#include "ast/functiontype.hpp"
#include "ast/statement.hpp"
#include "ast/expression.hpp"

/******************************************************************************
 * Extra Types                                                                *
 ******************************************************************************/

DEFINE_EXCEPTION(ParserException, "an error occurred during the parsing process");

/******************************************************************************
 * Parse Function                                                             *
 ******************************************************************************/

template <class T>
shared_ptr<T> parse(istream& stream, shared_ptr<Messages> messages = shared_ptr<Messages>(), const string& filename = string()) throw (ParserException);

template <class T>
shared_ptr<T> parse(const string& code, shared_ptr<Messages> messages = shared_ptr<Messages>(), const string& filename = string()) throw (ParserException);

/******************************************************************************
 * Function Implementation                                                    *
 ******************************************************************************/

/*
#define DEBUG_PARSER
//*/

namespace {
	template <class T>
	int entry_point() {
		#define ENTRY_POINT(Class, Token) \
			if (typeid(T) == typeid(Class)) \
				return yy::BisonParser::token::Token
		
		ENTRY_POINT( Declarations, ENTER_DECLARATIONS );
		ENTRY_POINT( FunctionType, ENTER_FUNCTIONTYPE );
		ENTRY_POINT( Statement,    ENTER_STATEMENT    );
		ENTRY_POINT( Expression,   ENTER_EXPRESSION   );
		
		assert(false);
		
		#undef ENTRY_POINT
	}
}

template <class T>
shared_ptr<T> parse(istream& stream, shared_ptr<Messages> messages, const string& filename) throw (ParserException) {
	yyscan_t scanner;
	
	scoped_ptr<LexerParams> lexerParams(new LexerParams);
	lexerParams->in = &stream;
	lexerParams->messages = messages;
	lexerParams->entry_point = entry_point<T>();
	yylex_init_extra (lexerParams.get(), &scanner);
	
	shared_ptr<Node> result;
	scoped_ptr<yy::BisonParser> parser(new yy::BisonParser(result, scanner, messages, filename));
	#ifdef DEBUG_PARSER
		parser->set_debug_level(true);
	#endif
	int parseSuccess = parser->parse();
	yylex_destroy(scanner);
	
	if (parseSuccess == 1)
		throw ParserException();
	else if (parseSuccess == 2)
		throw ParserException("The parser ran out of memory.");

	return dynamic_pointer_cast<T>(result);
}

template <class T>
shared_ptr<T> parse(const string& code, shared_ptr<Messages> messages, const string& filename) throw (ParserException) {
	istringstream strout(code);
	
	return parse<T>(strout, messages, filename);
}

#endif // PARSER_HPP
